/* SPDX-License-Identifier: GPL-2.0 */
/* rwsem.h: R/W semaphores, public interface
 *
 * Written by David Howells (dhowells@redhat.com).
 * Derived from asm-i386/semaphore.h
 */

#ifndef SEMINIX_RWSEM_H
#define SEMINIX_RWSEM_H

#include <utils/types.h>
#include <utils/err.h>
#include <utils/list.h>
#include <utils/atomic.h>
#include <seminix/linkage.h>
#include <seminix/lock/spinlock_types.h>

struct rw_semaphore;

/* All arch specific implementations share the same struct */
struct rw_semaphore {
    long			count;
    raw_spinlock_t		wait_lock;
    struct list_head	wait_list;
};

extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem);
extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem);
extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *);
extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem);

/* Include the arch specific part */
#include <asm/rwsem.h>

/* In all implementations count != 0 means locked */
static inline int rwsem_is_locked(struct rw_semaphore *sem)
{
    return sem->count != 0;
}

#define __RWSEM_DEP_MAP_INIT(lockname)

#define __RWSEM_INITIALIZER(name)			\
    { RWSEM_UNLOCKED_VALUE,				\
      __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),	\
      LIST_HEAD_INIT((name).wait_list)		\
      __RWSEM_DEP_MAP_INIT(name) }

#define DECLARE_RWSEM(name) \
    struct rw_semaphore name = __RWSEM_INITIALIZER(name)

extern void __init_rwsem(struct rw_semaphore *sem, const char *name);

#define init_rwsem(sem)						\
do {								\
    __init_rwsem((sem), #sem);			\
} while (0)

/*
 * lock for reading
 */
extern void down_read(struct rw_semaphore *sem);

/*
 * trylock for reading -- returns 1 if successful, 0 if contention
 */
extern int down_read_trylock(struct rw_semaphore *sem);

/*
 * lock for writing
 */
extern void down_write(struct rw_semaphore *sem);

/*
 * trylock for writing -- returns 1 if successful, 0 if contention
 */
extern int down_write_trylock(struct rw_semaphore *sem);

/*
 * release a read lock
 */
extern void up_read(struct rw_semaphore *sem);

/*
 * release a write lock
 */
extern void up_write(struct rw_semaphore *sem);

/*
 * downgrade write lock to read lock
 */
extern void downgrade_write(struct rw_semaphore *sem);

#define down_read_nested(sem, subclass)		down_read(sem)
#define down_write_nest_lock(sem, nest_lock)	down_write(sem)
#define down_write_nested(sem, subclass)	down_write(sem)
#define down_read_non_owner(sem)		down_read(sem)
#define up_read_non_owner(sem)			up_read(sem)

#endif /* !SEMINIX_RWSEM_H */
